#pragma once

#include <semaphore.h>
#include <pthread.h>
#include <vector>

const static int g_cap = 5;

template<class T>
class ringqueue
{
public:
    ringqueue(const int cap = g_cap)
        : _cap(cap)
        , _ring(cap)
    {
        sem_init(&_data, 0, 0);
        sem_init(&_room, 0, _cap);
        pthread_mutex_init(&_data_mutex, nullptr);
        pthread_mutex_init(&_room_mutex, nullptr);
    }

    void Push(const T in)
    {
        sem_wait(&_room);
        pthread_mutex_lock(&_room_mutex);
        _ring[_room_pos++] = in;
        _room_pos = _room_pos % _cap;
        pthread_mutex_unlock(&_room_mutex);
        sem_post(&_data);
    }

    void Pop(T* out)
    {
        sem_wait(&_data);
        pthread_mutex_lock(&_data_mutex);
        *out = _ring[_data_pos++];
        _data_pos = _data_pos % _cap;
        pthread_mutex_unlock(&_data_mutex);
        sem_post(&_room);
    }

    ~ringqueue()
    {
        sem_destroy(&_data);
        sem_destroy(&_room);
        pthread_mutex_destroy(&_data_mutex);
        pthread_mutex_destroy(&_room_mutex);
    }
private:
    int _cap;
    
    sem_t _data;
    sem_t _room;
    
    size_t _data_pos;
    size_t _room_pos;
    std::vector<int> _ring;

    pthread_mutex_t _data_mutex;
    pthread_mutex_t _room_mutex;
};